package com.xlh.virtualization.controller;

import com.google.common.collect.Lists;
import com.xlh.aop.PreAcquireLock;
import com.xlh.common.Result;
import com.xlh.dto.course.ChapterBasicInfoDTO;
import com.xlh.exception.common.GlobalException;
import com.xlh.security.SpringSecurityUserInfoHolder;
import com.xlh.security.UserInfoHolder;
import com.xlh.service.course.ChapterExtendService;
import com.xlh.service.course.ChapterService;
import com.xlh.support.experimentshare.domain.ShareUser;
import com.xlh.support.experimentshare.service.ExperimentShareService;
import com.xlh.virtualization.domain.ContainerContext;
import com.xlh.virtualization.domain.ExperimentContainer;
import com.xlh.virtualization.domain.ExperimentContext;
import com.xlh.virtualization.job.delayed.RedirectToTheoryListener;
import com.xlh.virtualization.job.delayed.RedirectToTheoryTask;
import com.xlh.virtualization.po.FlavorUpdatePO;
import com.xlh.virtualization.properties.ContainerProperties;
import com.xlh.virtualization.service.ContainerService;
import com.xlh.virtualization.service.ExperimentContainerService;
import com.xlh.virtualization.service.ExperimentService;
import com.xlh.virtualization.service.ImageAllocationService;
import com.xlh.virtualization.vo.ExperimentContainerStudentVO;
import com.xlh.virtualization.vo.ExperimentVO;
import com.xlh.virtualization.vo.FlavorVO;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.apache.commons.collections.CollectionUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.*;

import javax.validation.Valid;
import java.io.File;
import java.util.List;
import java.util.Map;

import static com.xlh.virtualization.constant.ContextTypeEnum.COURSE;
import static com.xlh.virtualization.constant.ContextTypeEnum.TASK;
import static com.xlh.virtualization.constant.ExperimentConstant.*;

/**
 * @author cheer
 */
@Api("课程实验")
@RestController
@RequestMapping("/experiment/course")
public class ExperimentCourseController extends BaseExperimentController {

    @Autowired
    public ExperimentCourseController(UserInfoHolder userInfoHolder, ExperimentService experimentService,
                                      ContainerService containerService) {
        super.userInfoHolder = userInfoHolder;
        super.experimentService = experimentService;
        super.containerService = containerService;
    }

    @Autowired
    private ContainerProperties containerProperties;

    @Autowired
    private ExperimentShareService experimentShareService;

    @Autowired
    private ImageAllocationService imageAllocationService;

    @Autowired
    private ChapterService chapterService;

    @Autowired
    private ExperimentContainerService experimentContainerService;

    @Autowired
    private ChapterExtendService chapterExtendService;

    @ApiOperation("课程中查询开始实验提示（是否有其它实验正在进行）")
    @GetMapping("/{taskId}/prompt")
    @PreAuthorize("@permissionValidator.isTeacherOrStudent()")
    public Result<Boolean> queryPrompt(@PathVariable("taskId") Long taskId) {
        ContainerContext containerContext = getContainerContext(userInfoHolder.getUserId(), taskId);
        if (experimentService.isOtherExperimentRunning(containerContext)) {
            return Result.success(true);
        }
        return Result.success(false);
    }

    @ApiOperation("课程中进入实验（获取虚拟机）/ 刷新")
    @GetMapping("/{taskId}")
    @PreAuthorize("@permissionValidator.isTeacherOrStudent()")
    @PreAcquireLock(value = ENTER_EXPERIMENT_KEY, timeout = ENTER_EXPERIMENT_TIME_OUT,
            message = ENTER_EXPERIMENT_LOCK_MESSAGE)
    public Result<ExperimentVO> enterExperiment(@PathVariable("taskId") Long taskId,
                                                @Autowired SpringSecurityUserInfoHolder userInfoHolder) {
        return super.enterExperiment(taskId);
    }

    @ApiOperation("课程中初始化实验")
    @GetMapping("/{taskId}/init")
    @PreAuthorize("@permissionValidator.isTeacherOrStudent()")
    @PreAcquireLock(value = ENTER_EXPERIMENT_KEY, timeout = ENTER_EXPERIMENT_TIME_OUT,
            message = ENTER_EXPERIMENT_LOCK_MESSAGE)
    public Result<ExperimentVO> initExperiment(@PathVariable("taskId") Long taskId,
                                               @Autowired SpringSecurityUserInfoHolder userInfoHolder) {
        return super.initExperiment(taskId);
    }

    @ApiOperation("课程中查询配置")
    @GetMapping("/{taskId}/flavor")
    @PreAuthorize("@permissionValidator.isTeacher()")
    public Result<FlavorVO> queryFlavor(@PathVariable("taskId") Long taskId) {
        FlavorVO flavorVO = imageAllocationService.getFlavorByContext(TASK.getCode(), taskId);
        return Result.success(flavorVO);
    }

    @ApiOperation("课程中保存配置")
    @PostMapping("/{taskId}/flavor")
    @PreAuthorize("@permissionValidator.isTeacher()")
    public Result updateFlavor(@PathVariable("taskId") Long taskId,
                               @RequestBody @Valid FlavorUpdatePO flavorUpdatePO) {
        imageAllocationService.updateFlavorByContext(TASK.getCode(), taskId, flavorUpdatePO);
        return Result.success();
    }

    @ApiOperation("课程中压缩文件")
    @GetMapping("/{taskId}/zip")
    @PreAuthorize("@permissionValidator.isTeacherOrStudent()")
    public Result zipFile(@PathVariable("taskId") Long taskId) {
        ContainerContext containerContext = getContainerContext(userInfoHolder.getUserId(), taskId);
        String filePath = getVolumePath(containerContext);
        experimentContainerService.zipFile(filePath);
        return Result.success();
    }

    @ApiOperation("课程中下载文件")
    @GetMapping("/{taskId}/download")
    @PreAuthorize("@permissionValidator.isTeacherOrStudent()")
    public void downloadFile(@PathVariable("taskId") Long taskId) {
        List<ChapterBasicInfoDTO> list = chapterService.listChapterInfo(null, Lists.newArrayList(taskId), null);
        if (CollectionUtils.isEmpty(list)) {
            throw new GlobalException("任务不存在");
        }
        String name = list.get(0).getName();

        ContainerContext containerContext = getContainerContext(userInfoHolder.getUserId(), taskId);
        String filePath = getVolumePath(containerContext);
        String fileName = userInfoHolder.getUsername() + "-" + name + "-代码.zip";
        experimentContainerService.downloadFile(filePath, fileName);
    }

    @ApiOperation("批阅学生实验环境")
    @GetMapping("/{taskId}/{studentId}")
    @PreAuthorize("@permissionValidator.isTeacher()")
    public Result<List<ExperimentContainerStudentVO>> enterStudentExperiment(@PathVariable("taskId") Long taskId,
                                                                             @PathVariable("studentId") Long userId) {

        ContainerContext containerContext = getContainerContext(userId, taskId);
        List<ExperimentContainerStudentVO> experimentContainerStudentVOS = experimentService.getStudentExperiment(containerContext);
        return Result.success(experimentContainerStudentVOS);
    }

    @ApiOperation("停止实验环境")
    @GetMapping("/{taskId}/stop")
    @PreAuthorize("@permissionValidator.isTeacherOrStudent()")
    public Result stopExperiment(@PathVariable("taskId") Long taskId) {
        ContainerContext containerContext = getContainerContext(userInfoHolder.getUserId(), taskId);
        containerService.stopContainerByContext(containerContext);
        return Result.success();
    }

    @ApiOperation("延时一小时")
    @GetMapping("/{taskId}/delay")
    @PreAuthorize("@permissionValidator.isTeacherOrStudent()")
    public Result delayExperiment(@PathVariable("taskId") Long taskId) {
        ContainerContext containerContext = getContainerContext(userInfoHolder.getUserId(), taskId);
        RedirectToTheoryTask task = new RedirectToTheoryTask(containerContext,
                RedirectToTheoryTask.TaskTypeEnum.DELAY);
        RedirectToTheoryListener.putTask(task);
        return Result.success();
    }

    @Override
    protected ContainerContext getContainerContext(Long userId, Long taskId) {
        ContainerContext containerContext = new ContainerContext();
        containerContext.setUserId(userId);

        Map<String, Object> map = chapterExtendService.getCourseInfo(taskId);
        if (map == null || map.get("courseImageLoaded") == null) {
            throw new GlobalException("该任务没有对应的章节或课程");
        }

        if ((boolean) map.get("courseImageLoaded")) {
            if (map.get("courseId") == null) {
                throw new GlobalException("该任务没有对应的章节或课程");
            }

            containerContext.setContextType(COURSE.getCode());
            containerContext.setContextId((Long) map.get("courseId"));

        } else {
            containerContext.setContextType(TASK.getCode());
            containerContext.setContextId(taskId);
        }

        return containerContext;
    }

    @Override
    protected ExperimentContext getExperimentContext(Long userId, Long taskId) {
        return new ExperimentContext()
                .setUserId(userId)
                .setContextType(TASK.getCode())
                .setContextId(taskId);
    }

    @Override
    protected void preEnterExperiment(ContainerContext containerContext, ExperimentContext experimentContext) {
        // 失效缓存的分享虚机信息
        ShareUser shareUser = ShareUser.builder()
                .userId(containerContext.getUserId())
                .chapterId(experimentContext.getContextId())
                .build();
        experimentShareService.invalidShareExperiment(shareUser);

        experimentService.stopOtherExperiment(containerContext);
    }

    @Override
    protected void postEnterExperiment(ContainerContext containerContext, ExperimentContext experimentContext,
                                       List<ExperimentContainer> experimentContainers) {
        // 缓存虚机信息供分享使用
        ShareUser shareUser = ShareUser.builder()
                .userId(containerContext.getUserId())
                .chapterId(experimentContext.getContextId())
                .username(userInfoHolder.getUsername())
                .build();
        experimentShareService.cacheShareExperiment(shareUser, experimentContainers);

        try {
            String path = getVolumePath(containerContext);
            new File(path).setWritable(true, false);
        } catch (Exception ignore) {
        }
    }

    @Override
    protected void handleEnterExperimentException(ContainerContext containerContext, ExperimentContext experimentContext) {
        ShareUser shareUser = ShareUser.builder()
                .userId(containerContext.getUserId())
                .chapterId(experimentContext.getContextId())
                .build();
        experimentShareService.closeShareExperiment(Lists.newArrayList(shareUser));
    }

    @Override
    protected String getVolumePath(ContainerContext containerContext) {
        return containerProperties.getNfsDir() + "/course/c" + containerContext.getContextType() + containerContext.getContextId()
                + "/u" + containerContext.getUserId() + "/";
    }


    // 测试使用
    @ApiOperation("课程中删除实验")
    @DeleteMapping("/{taskId}")
    @PreAuthorize("@permissionValidator.isTeacherOrStudent()")
    @PreAcquireLock(value = ENTER_EXPERIMENT_KEY)
    public Result deleteExperiment(@PathVariable("taskId") Long taskId,
                                   @Autowired SpringSecurityUserInfoHolder userInfoHolder) {
        ContainerContext containerContext = getContainerContext(userInfoHolder.getUserId(), taskId);
        containerService.deleteContainerByContext(containerContext);
        return Result.success();
    }

}
